package com.czkevin.biliwsclient;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.czkevin.biliwsclient.lottery.data.LotteryAwardInfo;
import com.czkevin.biliwsclient.lottery.data.LotteryStartInfo;
import com.czkevin.biliwsclient.data.SentGift;
import com.czkevin.biliwsclient.data.SentGuard;
import com.czkevin.biliwsclient.data.User;
import com.czkevin.biliwsclient.gift.CoinType;
import com.czkevin.biliwsclient.gift.GiftInfo;

import java.nio.ByteBuffer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public abstract class BiliPacketAdapter implements BiliLiveClient.BiliPacketCallback {

  /**
   * 审核通过
   */
  public static final int LOTTERY_STATUS_AUTHORIZED = 4;
  public static final int LOTTERY_STATUS_FORBIDDEN = 5;

  @Override
  public void onConnected() { }

  @Override
  public void onClose(Exception ex) { }

  @Override
  public void onReceive(BiliPacket packet) {
    if(packet.action == 5) {
      // JSON数据包
      JSONObject j = packet.getDataAsJSON();
      String cmd = j.getString("cmd");
      if("DANMU_MSG".equals(cmd) || cmd.startsWith("DANMU_MSG:")) {
        _internalDanmu(packet);
      } else if ("SEND_GIFT".equals(cmd)) {
        _internalGift(packet);
      } else if ("GUARD_BUY".equals(cmd)) {
        _internalGuard(packet);
      } else if ("ENTRY_EFFECT".equals(cmd)) {
        _internalEntryEffect(packet);
      } else if ("INTERACT_WORD".equals(cmd)) {
        final JSONObject data = j.getJSONObject("data");
        final int msg_type = data.getIntValue("msg_type");
        final String uname = data.getString("uname");
        final int uid = data.getIntValue("uid");
        if(msg_type == 1) {
          onEnterRoom(uid, uname);
        } else if (msg_type == 2 || msg_type == 4) {
          onFollow(uid, uname, msg_type == 4);
        } else if (msg_type == 3) {
          onShare(uid, uname);
        }
      } else if ("ANCHOR_LOT_CHECKSTATUS".equals(cmd)) {
        final JSONObject data = j.getJSONObject("data");
        final long id = data.getLongValue("id");
        final int status = data.getIntValue("status");
        final long uid = data.getLongValue("uid");
        onLotteryCheckStatus(id, status, uid);
      } else if ("ANCHOR_LOT_START".equals(cmd)) {
        onLotteryStart(j.getObject("data", LotteryStartInfo.class));
      } else if ("ANCHOR_LOT_END".equals(cmd)) {
        final JSONObject data = j.getJSONObject("data");
        onLotteryEnd(data.getIntValue("id"));
      } else if ("ANCHOR_LOT_AWARD".equals(cmd)) {
        onLotteryAward(j.getObject("data", LotteryAwardInfo.class));
      }
    } else if(packet.action == 3) {
      _internalPopularity(packet);
    } else if (packet.action == 8) {
      onAuthenticated();
    }
  }

  protected void onAuthenticated() { }

  protected void onEnterRoom(long uid, String uname) { }

  protected void onFollow(long uid, String uname, boolean special) { }

  protected void onShare(long uid, String uname) { }

  protected void onPopularityChange(int popularity) { }

  protected void onDanmu(long uid, String username, String text) { }

  protected void onGift(User user, SentGift sentGift, String action) { }

  protected void onGuard(User user, SentGuard sentGuard) { }

  protected void onEntryEffect(long uid, int level, String uname) { }

  protected void onLotteryCheckStatus(long id, int status, long uid) { }

  protected void onLotteryStart(LotteryStartInfo lotteryStartInfo) { }

  /**
   * 实际调用可能早于 {onLotteryAward()} ！！！
   * @param id 天选id
   */
  protected void onLotteryEnd(int id){ }

  protected void onLotteryAward(LotteryAwardInfo lotteryAwardInfo) { }

  private void _internalPopularity(BiliPacket packet) {
    final int popularity = ByteBuffer.wrap(packet.getData()).getInt();
    onPopularityChange(popularity);
  }

  private void _internalDanmu(BiliPacket packet) {
    JSONArray d = packet.getDataAsJSON().getJSONArray("info");
    final long uid = d.getJSONArray(2).getLongValue(0);
    final String username = d.getJSONArray(2).getString(1);
    final String text = d.getString(1);
    onDanmu(uid, username, text);
  }

  protected void _internalGift(BiliPacket p) {
    JSONObject j = p.getDataAsJSON().getJSONObject("data");
    int num = j.getIntValue("num");
    long uid = j.getLongValue("uid");

    String uname = j.getString("uname");
    String action = j.getString("action");

    GiftInfo g = new GiftInfo(j.getIntValue("giftId"), j.getString("giftName"), j.getIntValue("price"), j.getIntValue("giftType"), CoinType.valueOf(j.getString("coin_type").toUpperCase()));
    User u = new User(uid, uname);
    SentGift sent = new SentGift(g, num, j.getIntValue("total_coin"), j.getString("coin_type"));
    onGift(u, sent, action);
  }

  private void _internalGuard(BiliPacket p) {
    JSONObject data = p.getDataAsJSON().getJSONObject("data");
    final int guardLevel = data.getIntValue("guard_level");
    final int months = data.getIntValue("num");
    final int price = data.getIntValue("price");

    final int giftId = data.getIntValue("gift_id");
    final String giftName = data.getString("gift_name");

    final long uid = data.getLongValue("uid");
    final String uname = data.getString("username");

    onGuard(new User(uid, uname), new SentGuard(guardLevel, months, giftId, giftName, price));
  }

  private static final Pattern PATTERN_ENTRY_EFFECT_UNAME = Pattern.compile("<%(.+)%>");

  protected void _internalEntryEffect(BiliPacket packet) {
    JSONObject data = packet.getDataAsJSON().getJSONObject("data");
    final long uid = data.getLongValue("uid");
    final int level = data.getIntValue("privilege_type");
    final String uname;
    final Matcher matcher = PATTERN_ENTRY_EFFECT_UNAME.matcher(
        data.containsKey("copy_writing_v2") ? data.getString("copy_writing_v2") : data.getString("copy_writing")
    );
    if(matcher.matches()) {
      uname = matcher.group(1);
    } else {
      uname = Long.toString(uid);
    }
    onEntryEffect(uid, level, uname);
  }

}
